﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using SocialApi.Core.Extensions;
using SocialApi.Core.Request;
using SocialApi.Instagram.AppSettings;

namespace SocialApi.Instagram.Entities.Methods.Media
{
    internal class MediaMethods : IMediaMethods
    {
        #region Члены IMediaMethods

        #region Get

        /// <summary>
        /// Получение детальной информации о медиа
        /// </summary>
        /// <param name="mediaId">Id текущей медиа, информацию которой хотим получить</param>
        public IResult Media(long mediaId)
        {
            var url = InstagramService.GetMethodUrl(ApiUrls.ApiBaseUrl, ApiUrls.Media, mediaId.ToInvariantString());
            var jObject = JObject.Parse(Navigator.Instance.Get(url));
            InstagramService.CheckResult(jObject);
            return jObject["data"].ToObject<Result>();
        }

        /// <summary>
        /// Асинхронное получение детальной информации о медиа
        /// </summary>
        /// <param name="mediaId">Id текущей медиа, информацию которой хотим получить</param>
        public async Task<IResult> MediaAsync(long mediaId)
        {
            var url = InstagramService.GetMethodUrl(ApiUrls.ApiBaseUrl, ApiUrls.Media, mediaId.ToInvariantString());
            var result = await Navigator.Instance.GetAsync(url);
            var jObject = JObject.Parse(result);
            InstagramService.CheckResult(jObject);
            return jObject["data"].ToObject<Result>();
        }

        #endregion

        #region Popular

        /// <summary>
        /// Получение списка популярных медиа
        /// </summary>
        public IEnumerable<IResult> Popular()
        {
            var url = InstagramService.GetMethodUrl(ApiUrls.ApiBaseUrl, ApiUrls.Media, "popular");
            var jObject = JObject.Parse(Navigator.Instance.Get(url));
            InstagramService.CheckResult(jObject);
#if NET_4_5 || NET_4_0
            return jObject["data"].ToObject<IEnumerable<Result>>();
#else
            return jObject["data"].ToObject<List<Result>>() as IEnumerable<IResult>;
#endif
        }

        /// <summary>
        /// Асинхронное получение списка популярных медиа
        /// </summary>
        public async Task<IEnumerable<IResult>> PopularAsync()
        {
            var url = InstagramService.GetMethodUrl(ApiUrls.ApiBaseUrl, ApiUrls.Media, "popular");
            var result = await Navigator.Instance.GetAsync(url);
            var jObject = JObject.Parse(result);
            InstagramService.CheckResult(jObject);
#if NET_4_5 || NET_4_0
            return jObject["data"].ToObject<IEnumerable<Result>>();
#else
            return jObject["data"].ToObject<List<Result>>() as IEnumerable<IResult>;
#endif
        }

        #endregion

        #region Search

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        public IEnumerable<IResult> Search(int distance)
        {
            return Search(1000, 50, 50, null, null);
        }

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        public IEnumerable<IResult> Search(double latitude, double longitude)
        {
            return Search(1000, latitude, longitude, null, null);
        }

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        public IEnumerable<IResult> Search(int distance, double latitude, double longitude)
        {
            return Search(distance, latitude, longitude, null, null);
        }

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public IEnumerable<IResult> Search(TimeSpan? min, TimeSpan? max)
        {
            return Search(1000, 50, 50, min, max);
        }

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public IEnumerable<IResult> Search(int distance, TimeSpan? min, TimeSpan? max)
        {
            return Search(distance, 50, 50, min, max);
        }

        /// <summary>
        /// Поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public IEnumerable<IResult> Search(int distance, double latitude, double longitude, TimeSpan? min, TimeSpan? max)
        {
            var url = CreateSearchUrl(distance, latitude, longitude, min, max);
            var jObject = JObject.Parse(Navigator.Instance.Get(url));
            InstagramService.CheckResult(jObject);
#if NET_4_5 || NET_4_0
            return jObject["data"].ToObject<IEnumerable<Result>>();
#else
            return jObject["data"].ToObject<List<Result>>() as IEnumerable<IResult>;
#endif
        }
        
        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        public async Task<IEnumerable<IResult>> SearchAsync(int distance)
        {
            var result = await SearchAsync(distance, 50, 50, null, null);
            return result;
        }

        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        public async Task<IEnumerable<IResult>> SearchAsync(double latitude, double longitude)
        {
            var result = await SearchAsync(1000, latitude, longitude, null, null);
            return result;
        }

        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        public async Task<IEnumerable<IResult>> SearchAsync(int distance, double latitude, double longitude)
        {
            var result = await SearchAsync(distance, latitude, longitude, null, null);
            return result;
        }

        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public async Task<IEnumerable<IResult>> SearchAsync(TimeSpan? min, TimeSpan? max)
        {
            var result = await SearchAsync(1000, 50, 50, min, max);
            return result;
        }

        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public async Task<IEnumerable<IResult>> SearchAsync(int distance, TimeSpan? min, TimeSpan? max)
        {
            var result = await SearchAsync(distance, 50, 50, min, max);
            return result;
        }

        /// <summary>
        /// Асинхронное поиск медиа
        /// </summary>
        /// <param name="distance">Дистанция в метрах (по умолчанию 1000)</param>
        /// <param name="latitude">Широта</param>
        /// <param name="longitude">Долгота</param>
        /// <param name="min">Минимальная временная метка в UNIX - формате</param>
        /// <param name="max">Максимальная временная метка в UNIX - формате</param>
        public async Task<IEnumerable<IResult>> SearchAsync(int distance, double latitude, double longitude, TimeSpan? min, TimeSpan? max)
        {
            var url = CreateSearchUrl(distance, latitude, longitude, min, max);
            var result = await Navigator.Instance.GetAsync(url);
            var jObject = JObject.Parse(result);
            InstagramService.CheckResult(jObject);
#if NET_4_5 || NET_4_0
            return jObject["data"].ToObject<IEnumerable<Result>>();
#else
            return jObject["data"].ToObject<List<Result>>() as IEnumerable<IResult>;
#endif
        }

        public static string CreateSearchUrl(int distance, double latitude, double longitude, TimeSpan? min, TimeSpan? max)
        {
            var result = new StringBuilder(InstagramService.GetMethodUrl(ApiUrls.ApiBaseUrl, ApiUrls.Media, ApiUrls.Search));
            result.AppendFormat("&distance={0}&lat={1}&lng={2}", distance, latitude, longitude);
            if (min.Exists())
            {
                if (min.HasValue)
                {
                    result.AppendFormat("&min_timestamp={0}", min);
                }
            }
            if (max.Exists())
            {
                if (max.HasValue)
                {
                    result.AppendFormat("&max_timestamp={0}", max);
                }
            }
            return result.ToString();
        }

        #endregion

        #endregion
    }
}